﻿/*************************************************
Http网络请求处理类
author：Daixiwei
**************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace taurus.client
{
    public class HttpClient : ISocketLayer
    {
		readonly static byte[] NULL_BYTES = new byte[0];
        const string _Method = "POST";
        const string _ContentType = "application/octet-stream";

        const string _Result = "$r";
        const string _Param = "$p";
        const string _Session = "$s";
        const string _Version = "$v";
        string _url;
        string _session_id= string.Empty;

        readonly Queue<Action> _actionQueue = new Queue<Action>();
        Dictionary<Message, bool> _messageMap = new Dictionary<Message, bool>();

        public HttpClient(string url)
        {
            _url = url;
            ServicePointManager.DefaultConnectionLimit = 5;
        }

        private void __sendMsg(Message msg)
        {
            var bytes = NULL_BYTES;
            var _data = TObject.newInstance();
            _data.putString(_Session, _session_id);
            _data.putInt(_Version, NetManager.Version);
            if (msg.useText && msg.param == null)
            {
                if (string.IsNullOrEmpty(msg.textParam))
                {
                    msg.param = TObject.newInstance();
                }
                else
                {
                    try
                    {
                        msg.param = TObject.newFromJson(msg.textParam);
                    }
                    catch
                    {
                        msg.param = TObject.newInstance();
                    }
                }
            }
            _data.putTObject(_Param, msg.param);
            var buff = _data.toBinary();
            buff.compress();
            bytes = buff.bytes;

            __requestAsync(msg, bytes);
        }

        private void __requestAsync(Message msg,byte[] bytes)
        {
            var status = string.Empty;
            var code = 101;
            try
            {
                var request = (HttpWebRequest)HttpWebRequest.Create(new Uri(_url + msg.command));
                request.Method = _Method ;
                request.ContentType = _ContentType;
                request.ContentLength = bytes.Length;
                request.Timeout = NetManager.ConnectTimeOut*1000;
                request.ReadWriteTimeout = NetManager.WriteTimeOut*1000;
                using (var requestStream = request.GetRequestStream())
                {
                    requestStream.Write(bytes, 0, bytes.Length);
                    requestStream.Close();
                }
            
                var response = (HttpWebResponse)request.GetResponse();
                ByteArray buff = null;
                using (Stream responseStream = response.GetResponseStream())
                {
                    buff = new ByteArray();
                    while (true)
                    {
                        byte[] _buffer = new byte[1024];
                        var n = responseStream.Read(_buffer, 0, 1024);
                        if (n == 0) break;
                        buff.writeBytes(_buffer, 0, n);
                    }
                    responseStream.Close();
                    buff.uncompress();

                    var tem = TObject.newFromBinaryData(buff);
                    var result = tem.getInt(_Result);
                    msg.param = tem.getTObject(_Param);
                    if (msg.useText)
                    {
                        msg.textParam = msg.param.toJson();
                    }
                    code = result;
                }
            }
            catch (WebException ex)
            {
                status = ex.Message;
                if (ex.Status == WebExceptionStatus.Timeout)
                {
                    code = 102;
                }
            }
            catch (Exception e)
            {
                status = e.Message;
            }

            lock (_messageMap)
            {
                _messageMap.Remove(msg);
            }
            if (msg.callback != null)
            {
                var response = new MessageResponse();
                response.messageData = msg;
                response.returnCode = code;
                enqueueActionForDispatch(() =>
                {
                    if (code != 0)
                    {
                        Logger.error(status);
                    }
                    if (NetManager.DebugPrint)
                    {
                        var tem = code != 0 ? "status:" + status : string.Empty;
                        var data_str = msg.textParam != null ? msg.textParam : (msg.param != null ? msg.param.toJson() : "null");
                        var str = "Response [Web" + msg.command + "] ->result:" + response.returnCode + " param:" + data_str;
                        Logger.debug(str);
                    }
                    msg.callback(response);
                });
            }
        }

        private void enqueueActionForDispatch(Action action)
        {
            lock (_actionQueue)
            {
                _actionQueue.Enqueue(action);
            }
        }


        public void processEvent()
        {
            while (true)
            {
                Action myAction;
                lock (_actionQueue)
                {
                    if (_actionQueue.Count <= 0)
                    {
                        break;
                    }
                    myAction = _actionQueue.Dequeue();
                }
                myAction();
            }
        }

        public void disconnect()
        {
            
        }

        public void write(Message msg)
        {
            _messageMap.Add(msg, true);
            new Thread(() =>
            {
                msg.lastTime = DateTime.Now.Ticks;
                __sendMsg(msg);
            })
            {
                IsBackground = true
            }.Start();
        }

        public void setOnConnect(Action<SocketCode> onConnect)
        {
            
        }

        public void setOnEvent(Action<Message> onEvent)
        {
            
        }

        public int getAveragePingTime()
        {
            return 300;
        }

        public string getSession()
        {
            return this._session_id;
        }

        public void setSession(string session)
        {
            this._session_id = session;
        }

        public ConnectionProtocol getProtocol()
        {
            return ConnectionProtocol.Web;
        }

        public void connect()
        {
        }

        public void connect(ITObject validate)
        {
        }

        public void clearResponse()
        {
            lock (_messageMap)
            {
                foreach (KeyValuePair<Message, bool> kv in _messageMap)
                {
                    kv.Key.callback = null;
                }
                _messageMap.Clear();
            }
        }
    }
}
